﻿using System.Diagnostics;
using DimensionsHelper.LanguageService.Syntax;

namespace DimensionsHelper.LanguageService.Symbols;

/// <summary>
///     元数据的单个分类定义。在元数据中，单个分类的声明可分为如下几种情况：
///     <list type="number">
///         <item>
///             一般情况，带有确定的分类名。
///             <code>CategoryName["Label"][ use [\\.]List_Name][ Suffixes]</code>
///         </item>
///         <item>
///             忽略分类名，使用‘-’代替。
///             <code>- ["Label"][[ use [\\.]List_Name] | NA | DK | REF | Multiplier | Other]</code>
///             此时，文本标签、列表引用、NA、DK、Multiplier、Other之中必须有一个有效值，否则声明失败。
///             进行命名替换时，遵循如下顺序：List_Name > Other > Multiplier > DK > NA > REF，其中，List_Name
///             在语法上不会和其他四项一同出现。
///         </item>
///     </list>
///     在字段中声明分类符号时，需要额外注意区分带有列表引用的分类名和引用列表中的分类名之间的冲突情况。
///     例如：
///     <code>
/// List_A define
/// {
///     Category1, Category2
/// };
/// List_B define
/// {
///     use List_A, &lt;------- 在存储时，会创建一个名为List_A的MetadataCategorySymbol存储在列表中
///     Category1,  &lt;------- 此处的Category1会和List_A中的Category1命名冲突。
///     List_A      &lt;------- List_A在use List_A处已经被声明了，此处报命名重复错误。
/// };
/// </code>
/// </summary>
public class MetadataCategorySymbol : MetadataLabeledObjectSymbol
{
    private DomainSymbolTable? _categoriesSymbolTable;
    private MetadataFieldSymbol? _listReference;

    internal MetadataCategorySymbol(MetadataFieldSymbol containingField, MetadataCategory declaration,
        string? name = null) : base(SymbolKinds.MetadataCategory, containingField, declaration)
    {
        HasNamespace = declaration.Categories?.NamespaceKeywordToken != null;
        Name = name ?? declaration.Name;
    }


    public override string Name { get; }


    /// <summary>
    ///     如果此分类符号带有子列表，并且带有namespace关键字，返回<see langword="true" />。
    /// </summary>
    public bool HasNamespace { get; }


    /// <summary>
    ///     如果此分类符号使用use关键字引用了其他分类列表，此属性返回<see langword="true" />。
    /// </summary>
    public bool HasListReference => _listReference != null || ((MetadataCategory)Declaration).ListUsage != null;


    /// <summary>
    ///     向子列表声明一个分类定义。
    /// </summary>
    /// <param name="symbol">声明的分类符号</param>
    /// <returns>如果指定的符号名已存在，返回<see langword="false" /></returns>
    public bool DeclareLocalCategory(MetadataCategorySymbol symbol)
    {
        // 如果HasNamespace是false，声明时检查所在字段的分类列表
        if (!HasNamespace && ContainingSymbol is MetadataFieldSymbol field &&
            field.LookupCategory(symbol.Name) != null)
        {
            return false;
        }

        _categoriesSymbolTable ??= new DomainSymbolTable(this);
        return _categoriesSymbolTable.DeclareSymbol(symbol);
    }


    /// <summary>
    ///     查找指定名称的分类元素，此方法只在当前子分类符号表或者引用列表中查找，不支持使用全限定名查找。
    /// </summary>
    /// <param name="name">分类符号名，不区分大小写</param>
    public MetadataCategorySymbol? LookupLocalCategory(string name)
    {
        return _categoriesSymbolTable?.LookupLocalSymbol(name) as MetadataCategorySymbol ??
               _listReference?.LookupCategory(name) as MetadataCategorySymbol;
    }


    /// <summary>
    ///     设置分类符号引用的分类列表定义。
    /// </summary>
    /// <param name="elementList"></param>
    public void SetListReference(MetadataFieldSymbol elementList)
    {
        Debug.Assert(elementList.FieldKind == MetadataFieldKind.ElementList);
        _listReference = elementList;
    }


    public override bool Redeclare(ISymbol symbol)
    {
        bool res = !LocationMap.FileExists(symbol.GetFirstLocation().Uri);
        base.Redeclare(symbol);
        return res;
    }
}